package com.freewater.cargotracker.domain.model.handlingevent;

import com.freewater.cargotracker.domain.model.cargo.Cargo;
import com.freewater.cargotracker.domain.model.cargo.CargoRepository;
import com.freewater.cargotracker.domain.model.cargo.TrackingId;
import com.freewater.cargotracker.domain.model.location.Location;
import com.freewater.cargotracker.domain.model.location.LocationRepository;
import com.freewater.cargotracker.domain.model.location.UnLocode;
import com.freewater.cargotracker.domain.model.voyage.Voyage;
import com.freewater.cargotracker.domain.model.voyage.VoyageNumber;
import com.freewater.cargotracker.domain.model.voyage.VoyageRepository;

import java.time.LocalDateTime;

public class HandlingEventFactory {

    private final CargoRepository cargoRepository;
    private final LocationRepository locationRepository;
    private final VoyageRepository voyageRepository;

    public HandlingEventFactory(CargoRepository cargoRepository,
                                LocationRepository locationRepository,
                                VoyageRepository voyageRepository) {
        this.cargoRepository = cargoRepository;
        this.locationRepository = locationRepository;
        this.voyageRepository = voyageRepository;
    }

    public HandlingEvent createHandlingEvent(TrackingId cargoTrackingId,
                                             VoyageNumber voyageNumber,
                                             HandlingEvent.Type eventType,
                                             UnLocode locationUnLocode,
                                             LocalDateTime registrationTime,
                                             LocalDateTime completionTime)
            throws CannotCreateHandlingEventException {
        try {
            final Cargo cargo = this.findCargo(cargoTrackingId);
            final Voyage voyage = this.findVoyage(voyageNumber);
            final Location location = this.findLocation(locationUnLocode);
            if (voyage == null) {
                return new HandlingEvent(cargo, eventType, location, registrationTime, completionTime);
            } else {
                return new HandlingEvent(cargo, eventType, location, registrationTime, completionTime, voyage);
            }
        } catch (Exception e) {
            throw new CannotCreateHandlingEventException(e);
        }
    }

    private Cargo findCargo(TrackingId cargoTrackingId) throws UnknownCargoException {
        if (cargoTrackingId == null) {
            return null;
        }
        final Cargo cargo = this.cargoRepository.findByTrackingId(cargoTrackingId);
        if (cargo == null) {
            throw new UnknownCargoException(cargoTrackingId);
        }
        return cargo;
    }

    private Voyage findVoyage(VoyageNumber voyageNumber) throws UnknownVoyageException {
        if (voyageNumber == null) {
            return null;
        }
        final Voyage voyage = this.voyageRepository.findByVoyageNumber(voyageNumber);
        if (voyage == null) {
            throw new UnknownVoyageException(voyageNumber);
        }
        return voyage;
    }

    private Location findLocation(UnLocode unLocode) throws UnknownLocationException {
        if (unLocode == null) {
            return null;
        }
        final Location location = this.locationRepository.findByCode(unLocode);
        if (location == null) {
            throw new UnknownLocationException(unLocode);
        }
        return location;
    }
}
